home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / X11 / xconq / attack.c next >
C/C++ Source or Header  |  1995-05-09  |  13KB  |  428 lines

  1. /* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
  2. /* This program may be used, copied, modified, and redistributed freely */
  3. /* for noncommercial purposes, so long as this notice remains intact. */
  4.  
  5. #pragma comment(exestr, "@(#) attack.c 12.1 95/05/09 ")
  6.  
  7. /* RCS $Header: attack.c,v 1.3 88/07/17 16:18:22 shebs Exp $ */
  8.  
  9. /* Although conducting xconq combat is rather simple (i.e. try to move */
  10. /* into the hex), the outcome is under the control of many parameters. */
  11.  
  12. /* Rules of combat: the attacker hits the defender ("other") unit and its */
  13. /* occupants, but the damage does not take effect right away.  If counter */
  14. /* attacks are possible in this period, the defender always does so, with */
  15. /* the same odds.  If the defender dies, then the attacker moves into the */
  16. /* hex.  If the attacker dies, nothing happens.  If both survive, then the */
  17. /* attacker may attempt to capture the defender. */
  18.  
  19. #include "config.h"
  20. #include "misc.h"
  21. #include "period.h"
  22. #include "side.h"
  23. #include "unit.h"
  24. #include "map.h"
  25. #include "global.h"
  26.  
  27. extern int occdeath[];
  28.  
  29. char *summarize_units();
  30.  
  31. /* Buffers for verbal descriptions of units from each other's */
  32. /* point of view. */
  33.  
  34. char aabuf[BUFSIZE], aobuf[BUFSIZE], oabuf[BUFSIZE], oobuf[BUFSIZE];
  35. char hitbuf[BUFSIZE], killbuf[BUFSIZE];
  36.  
  37. /* Remember what the main units involved are, so display is handled relative */
  38. /* to them and not to any occupants. */
  39.  
  40. Unit *amain, *omain;
  41.  
  42. /* Hits on main units saved up, hits on occupants happen immediately. */
  43.  
  44. int ahit, ohit;
  45.  
  46. /* ... but the data is saved anyway, for message generation. */
  47.  
  48. int occhits[MAXUTYPES], occkills[MAXUTYPES];
  49.  
  50. /* Return true if the attacker defeated the defender, and can therefore */
  51. /* try to move into the defender's old position. */
  52.  
  53. attack(atker, other)
  54. Unit *atker, *other;
  55. {
  56.     int u, ax = atker->x, ay = atker->y, ox = other->x, oy = other->y;
  57.     Side *as = atker->side, *os = other->side;
  58.  
  59.     amain = atker;  omain = other;
  60.     ahit = ohit = 0;
  61.     for_all_unit_types(u) occhits[u] = occkills[u] = 0;
  62.     attack_unit(atker, other);
  63.     if (period.counterattack || utypes[atker->type].counterable)
  64.     attack_unit(other, atker);
  65.     reckon_damage();
  66.     if (see_exact(as, ax, ay))
  67.         draw_hex(as, ax, ay, TRUE);
  68.     if (see_exact(as, ox, oy))
  69.         draw_hex(as, ox, oy, TRUE);
  70.     if (see_exact(os, ax, ay))
  71.         draw_hex(os, ax, ay, TRUE);
  72.     if (see_exact(os, ox, oy))
  73.         draw_hex(os, ox, oy, TRUE);
  74.     all_see_hex(ax, ay);
  75.     all_see_hex(ox, oy);
  76.     attempt_to_capture_unit(atker, other);
  77.     return (alive(atker) && unit_at(ox, oy) == NULL);
  78. }
  79.  
  80. /* Test to see if enough ammo is available to make the attack. */
  81. /* Need enough of *all* types - semi-bogus but too complicated otherwise? */
  82.  
  83. enough_ammo(atker, other)
  84. Unit *atker, *other;
  85. {
  86.     int r;
  87.  
  88.     for_all_resource_types(r) {
  89.     if (utypes[other->type].hitby[r] > 0 &&
  90.         atker->supply[r] < utypes[atker->type].hitswith[r]) return FALSE;
  91.     }
  92.     return TRUE;
  93. }
  94.  
  95. /* Single attack, no counterattack.  Check and use ammo - usage independent */
  96. /* of outcome, but types used depend on unit types involved. */
  97.  
  98. attack_unit(atker, other)
  99. Unit *atker, *other;
  100. {
  101.     int r;
  102.  
  103.     wake_unit(other, FALSE);
  104.     if (alive(atker) && alive(other)) {
  105.     if (enough_ammo(atker, other)) {
  106.         hit_unit(atker, other);
  107.         for_all_resource_types(r) {
  108.         if (utypes[other->type].hitby[r] > 0) {
  109.             atker->supply[r] -= utypes[atker->type].hitswith[r];
  110.         }
  111.         }
  112.         atker->movesleft -= utypes[atker->type].hittime;
  113.     }
  114.     }
  115. }
  116.  
  117. /* Make a single hit and maybe hit some passengers also.  Power of hit */
  118. /* is constant, but chance is affected by neutrality, terrain, quality, */
  119. /* and occupants' protective abilities.  If a hit is successful, it may */
  120. /* have consequences on the defender's occupants, but limited by the */
  121. /* protection that the transport provides. */
  122.  
  123. hit_unit(atker, other)
  124. Unit *atker, *other;
  125. {
  126.     int chance, terr, hit = 0, a = atker->type, o = other->type;
  127.     Unit *occ;
  128.     Side *as = atker->side;
  129.  
  130.     chance = utypes[a].hit[o];
  131.     if (neutral(atker)) chance += period.neutrality;
  132.     terr = terrain_at(other->x, other->y);
  133.     chance -= (chance * utypes[o].defense[terr]) / 100;
  134.     if (utypes[a].maxquality > 0) {
  135.     chance += ((chance * atker->quality * utypes[a].skillf) /
  136.            utypes[a].maxquality) / 100;
  137.     }
  138.     if (utypes[o].maxquality > 0) {
  139.     chance -= ((chance * other->quality * utypes[o].disciplinef) /
  140.            utypes[o].maxquality) / 100;
  141.     }
  142.     for_all_occupants(other, occ) {
  143.     chance -= (chance * utypes[occ->type].protect[o]) / 100;
  144.     }
  145.     if (probability(chance)) hit = utypes[a].damage[o];
  146.     if (as != NULL) {
  147.     as->atkstats[a][o]++;
  148.     as->hitstats[a][o] += hit;
  149.     }
  150.     if (hit_unit_aux(atker, other, hit)) {
  151.     for_all_occupants(other, occ) {
  152.         if (probability(100 - utypes[o].protect[occ->type])) {
  153.         hit_unit(atker, occ);
  154.         }
  155.     }
  156.     }
  157. }
  158.  
  159. /* Do the hit itself.  Occupants are always hit/killed immediately, while */
  160. /* the "main units" of the hexes don't get it till later.  In any case, */
  161. /* messages are delayed.  Return true if the victim was hit but not killed. */
  162.  
  163. hit_unit_aux(atker, other, hit)
  164. Unit *atker, *other;
  165. int hit;
  166. {
  167.     int o = other->type, a = atker->type, chance, i;
  168.     Side *as = atker->side, *os = other->side;
  169.  
  170.     if (hit >= period.nukehit) {
  171.     notify_all("%s has been hit by a nuclear attack!!!",
  172.            unit_handle(NULL, other));
  173.     set_terrain_at(other->x, other->y,
  174.                ttypes[terrain_at(other->x, other->y)].nuked);
  175.     }
  176.     if (hit > 0 && mobile(o)) {
  177.     chance = utypes[o].retreat;
  178.     /* should adjust chance by morale etc */
  179.     if (probability(chance)) {
  180.         if (retreat_unit(other)) {
  181.         notify(as, "%s runs away!", unit_handle(as, other));
  182.         notify(os, "%s runs away!", unit_handle(os, other));
  183.         hit = 0;
  184.         }
  185.     }
  186.     }
  187.     if (other == omain) {
  188.     ohit = hit;
  189.     } else if (other == amain) {
  190.     ahit = hit;
  191.     } else {
  192.     if (hit >= other->hp) {
  193.         occkills[o]++;
  194.         kill_unit(other, COMBAT);
  195.         for_all_unit_types(i) occkills[i] += occdeath[i];
  196.     } else if (hit > 0) {
  197.         occhits[o]++;
  198.         other->hp -= hit;
  199.     }
  200.     if (utypes[a].selfdestruct) {
  201.         kill_unit(atker, COMBAT);
  202.     }
  203.     }
  204.     return (alive(other) && hit > 0);
  205. }
  206.  
  207. /* Hits on the main units have to be done later, so that mutual */
  208. /* destruction is possible.  This function also does all the notifying. */
  209. /* (Only the main units of a hex rate messages, occupants' fates are */
  210. /* summarized briefly.) */
  211.  
  212. reckon_damage()
  213. {
  214.     int o = omain->type, a = amain->type, i;
  215.     Side *as = amain->side, *os = omain->side;
  216.  
  217.     strcpy(aabuf, unit_handle(as, amain));
  218.     strcpy(aobuf, unit_handle(as, omain));
  219.     strcpy(oabuf, unit_handle(os, amain));
  220.     strcpy(oobuf, unit_handle(os, omain));
  221.  
  222.     if (ahit > 0) draw_blast(amain, omain->side, ahit);
  223.     if (ohit > 0) draw_blast(omain, amain->side, ohit);
  224.     if (ohit >= omain->hp) {
  225.     notify(as, "%s %s %s!", aabuf, utypes[o].destroymsg, aobuf);
  226.     notify(os, "%s %s %s!", oabuf, utypes[o].destroymsg, oobuf);
  227.     kill_unit(omain, COMBAT);
  228.     for_all_unit_types(i) occkills[i] += occdeath[i];
  229.     } else if (ohit > 0) {
  230.     notify(as, "%s hits %s!", aabuf, aobuf);
  231.     notify(os, "%s hits %s!", oabuf, oobuf);
  232.     omain->hp -= ohit;
  233.     } else {
  234.     /* messages about missing not too useful */
  235.     }
  236.     summarize_units(hitbuf, occhits);
  237.     summarize_units(killbuf, occkills);
  238.     if (strlen(hitbuf) > 0) {
  239.     if (strlen(killbuf) > 0) {
  240.         notify(as, "   (Also hit%s, killed%s)", hitbuf, killbuf);
  241.         notify(os, "   (%s hurt, %s killed)", hitbuf, killbuf);
  242.     } else {
  243.         notify(as, "   (Also hit%s)", hitbuf);
  244.         notify(os, "   (%s hurt)", hitbuf);
  245.     }
  246.     } else {
  247.     if (strlen(killbuf) > 0) {
  248.         notify(as, "   (Also killed%s)", killbuf);
  249.         notify(os, "   (%s killed)", killbuf);
  250.     }
  251.     }
  252.     if (ahit >= amain->hp) {
  253.     notify(as, "%s %s %s!", aobuf, utypes[a].destroymsg, aabuf);
  254.     notify(os, "%s %s %s!", oobuf, utypes[a].destroymsg, oabuf);
  255.     kill_unit(amain, COMBAT);
  256.     } else if (ahit > 0) {
  257.     notify(as, "%s hits %s!", aobuf, aabuf);
  258.     notify(os, "%s hits %s!", oobuf, oabuf);
  259.     amain->hp -= ahit;
  260.     } else {
  261.     /* messages about missing not too useful */
  262.     }
  263.     if (utypes[a].selfdestruct) kill_unit(amain, COMBAT);
  264.     if (utypes[o].selfdestruct) kill_unit(omain, COMBAT);
  265. }
  266.  
  267. /* Handle capture possibility and repulse/slaughter. */
  268.  
  269. /* The chance to capture an enemy is modified by several factors. */
  270. /* Neutrals have a different chance to be captured, and presence of */
  271. /* occupants should also has an effect.  Can't capture anything that is */
  272. /* on a kind of terrain that the capturer can't go on, unless victim has */
  273. /* "bridge effect". */
  274.  
  275. attempt_to_capture_unit(atker, other)
  276. Unit *atker, *other;
  277. {
  278.     int a = atker->type, o = other->type, chance;
  279.     int ox = other->x, oy = other->y;
  280.     Unit *occ;
  281.     Side *as = atker->side, *os = other->side;
  282.  
  283.     if (alive(atker) && alive(other) && could_capture(a, o)) {
  284.     if (impassable(atker, ox, oy) && !utypes[o].bridge[a]) return;
  285.     chance = utypes[a].capture[o];
  286.     if (neutral(other)) chance -= period.neutrality;
  287.     if (utypes[a].maxquality > 0) {
  288.         chance += ((chance * atker->quality * utypes[a].skillf) /
  289.                utypes[a].maxquality) / 100;
  290.     }
  291.     for_all_occupants(other, occ) {
  292.         chance -= (chance * utypes[occ->type].protect[o]) / 100;
  293.     }
  294.     if (probability(chance)) {
  295.         capture_unit(atker, other);
  296.         if (global.setproduct && utypes[o].maker) {
  297.         request_new_product(other);
  298.         other->movesleft = 1;
  299.         move_1(other->side, other);
  300.         }
  301.     } else if (atker->transport != NULL && 
  302.            (impassable(atker, ox, oy) ||
  303.             impassable(atker, atker->x, atker->y))) {
  304.         notify(as, "Resistance... %s was slaughtered!",
  305.            unit_handle(as, atker));
  306.         notify(os, "Resistance... %s was slaughtered!",
  307.            unit_handle(os, atker));
  308.         kill_unit(atker, COMBAT);
  309.     } else {
  310.         strcpy(aabuf, unit_handle(as, atker));
  311.         notify(as, "%s repulses %s!", unit_handle(as, other), aabuf);
  312.         strcpy(oabuf, unit_handle(os, atker));
  313.         notify(os, "%s repulses %s!", unit_handle(os, other), oabuf);
  314.     }
  315.     atker->movesleft -= utypes[a].hittime;
  316.     }
  317. }
  318.  
  319. /* There are many consequences of a unit being captured. */
  320. /* If the capturer is needed as a garrison, unload any occupants first. */
  321. /* (what if erstwhile occupants can't stay there?) */
  322.  
  323. capture_unit(unit, pris)
  324. Unit *unit, *pris;
  325. {
  326.     int u = unit->type, u2, px = pris->x, py = pris->y, i;
  327.     int occs[MAXUTYPES], gains[MAXUTYPES], kills[MAXUTYPES];
  328.     Unit *occ;
  329.     Side *ps = pris->side, *us = unit->side;
  330.  
  331.     for_all_unit_types(u2) occs[u2] = gains[u2] = kills[u2] = 0;
  332.     notify(us, "You captured %s!", unit_handle(us, pris));
  333.     notify(ps, "%s has been captured by the %s!",
  334.        unit_handle(ps, pris), plural_form(us->name));
  335.     if (global.setproduct) {
  336.     set_product(pris, NOTHING);
  337.     pris->schedule = 0;
  338.     }
  339.     for_all_occupants(pris, occ) {
  340.     occs[occ->type]++;
  341.     if (utypes[occ->type].changeside || could_capture(u, occ->type)) {
  342.         gains[occ->type]++;
  343.         /* side changing happens when whole unit changes */
  344.     } else {
  345.         kills[occ->type]++;
  346.         kill_unit(occ, COMBAT);
  347.         for_all_unit_types(i) kills[i] += occdeath[i];
  348.     }
  349.     }
  350.     summarize_units(hitbuf,  gains);
  351.     summarize_units(killbuf, kills);
  352.     summarize_units(spbuf, occs);
  353.     if (strlen(hitbuf) > 0) {
  354.     if (strlen(killbuf) > 0) {
  355.         notify(us, "   (Also captured%s, killed%s)", hitbuf, killbuf);
  356.     } else {
  357.         notify(us, "   (Also captured%s)", hitbuf);
  358.     }
  359.     } else if (strlen(killbuf) > 0) {
  360.     notify(us, "   (Also killed%s)", killbuf);
  361.     }
  362.     if (strlen(spbuf) > 0) {
  363.     notify(ps, "   (Lost%s)", spbuf);
  364.     }
  365.     /* The sad event itself */
  366.     unit_changes_side(pris, us, CAPTURE, PRISONER);
  367.     /* Guard the prisoner */
  368.     if (utypes[u].guard[pris->type] > 0) {
  369.     for_all_occupants(unit, occ) {
  370.         if (can_carry(pris, occ)) {
  371.         leave_hex(occ);
  372.         occupy_hex(occ, px, py);
  373.         }
  374.     }
  375.     /* This may kill some of the guard's occupants unnecessarily, but */
  376.         /* I can't think of a better solution */
  377.     kill_unit(unit, GARRISON);
  378.     } else if (can_carry(pris, unit)) {
  379.     leave_hex(unit);
  380.     occupy_hex(unit, px, py);
  381.     } else {
  382.     /* Capturer doesn't guard or enter, so nothing to do */
  383.     }
  384.     if (see_exact(ps, px, py))
  385.         draw_hex(ps, px, py, TRUE);
  386.     all_see_hex(px, py);
  387. }
  388.  
  389. /* Nearly-raw combat statistics are hard to interpret, but they provide */
  390. /* a useful check against subjective evaluation of performance. */
  391.  
  392. print_combat_results(fp, side)
  393. FILE *fp;
  394. Side *side;
  395. {
  396.     int a, d;
  397.  
  398.     fprintf(fp,
  399.         "Unit Performance (successes and attacks against enemy by type\n");
  400.     fprintf(fp, "   ");
  401.     for_all_unit_types(d) {
  402.     fprintf(fp, "  %c  ", utypes[d].uchar);
  403.     }
  404.     fprintf(fp, "\n");
  405.     for_all_unit_types(a) {
  406.     fprintf(fp, " %c ", utypes[a].uchar);
  407.     for_all_unit_types(d) {
  408.         if (side->atkstats[a][d] > 0) {
  409.         fprintf(fp, " %3.1f ",
  410.             ((float) side->hitstats[a][d]) /
  411.                      side->atkstats[a][d]);
  412.         } else {
  413.         fprintf(fp, "     ");
  414.         }
  415.     }
  416.     fprintf(fp, "\n   ");
  417.     for_all_unit_types(d) {
  418.         if (side->atkstats[a][d] > 0) {
  419.         fprintf(fp, " %3d ", side->atkstats[a][d]);
  420.         } else {
  421.         fprintf(fp, "     ");
  422.         }
  423.     }
  424.     fprintf(fp, "\n");
  425.     }
  426.     fprintf(fp, "\n");
  427. }
  428.